package cn.micai.base.concurrent;

import java.util.Calendar;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author zhaoxinguo on 2018/5/15.
 * @email sxdtzhaoxinguo@163.com
 * @qq 827358369
 * @phone 18611966723
 * @description 下面是一个使用读写锁的例子，创建几个写线程和读线程对 HashMap 中数据进行操作。
读线程的个数多于写线程，也就是说读取数据的频率高于修改数据的频率。使用读写锁比合
适。
 */
public class ReadWriteLockDemo {

    // 可重入读写锁
    private ReentrantReadWriteLock lock = null;
    // 读锁
    private Lock readLock = null;
    // 写锁
    private Lock writeLock = null;
    public int key = 100;
    public int index = 100;
    // 线程共享数据
    public Map<Integer, String> dataMap = null;

    public ReadWriteLockDemo() {
        lock = new ReentrantReadWriteLock(true);
        readLock = lock.readLock();
        writeLock = lock.writeLock();
        dataMap = new TreeMap<Integer, String>();
    }

    public static void main(String [] args) {
        ReadWriteLockDemo readWriteLockDemo = new ReadWriteLockDemo();
        // 第一次获取锁
        readWriteLockDemo.writeLock.lock();
        System.out.println(Thread.currentThread().getName() + " get writeLock.");
        // 第二次获取锁，应为是可重入锁
        readWriteLockDemo.writeLock.lock();
        System.out.println(Thread.currentThread().getName() + " get writeLock.");
        readWriteLockDemo.readLock.lock();
        System.out.println(Thread.currentThread().getName() + " get readLock.");
        readWriteLockDemo.readLock.lock();
        System.out.println(Thread.currentThread().getName() + " get readLock.");
        readWriteLockDemo.readLock.unlock();
        readWriteLockDemo.readLock.unlock();
        readWriteLockDemo.writeLock.unlock();
        readWriteLockDemo.writeLock.unlock();
        readWriteLockDemo.test();
    }

    public void test() {
        // 读线程比写线程多
        for (int i=0; i < 10; i++) {
            new Thread(new reader(this)).start();
        }
        for (int i=0; i < 3; i++) {
            new Thread(new writer(this)).start();
        }
    }

    public void read() {
        // 获取锁
        readLock.lock();
        try {
            if (dataMap.isEmpty()) {
                Calendar now = Calendar.getInstance();
                System.out.println(now.getTime().getTime() + " R "+ Thread.currentThread().getName() + " get key, but map is empty.");
            }
            String value = dataMap.get(index);
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime().getTime() + " R " + Thread.currentThread().getName() + " key=" + index + " value=" + value + " map size=" + dataMap.size());
            if (value != null) {
                index++;
            }
        } finally {
            // 释放锁
            readLock.unlock();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void write() {
        writeLock.lock();
        try {
            String value = "value" + key;
            dataMap.put(new Integer(key), value);
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime().getTime() + " W " + Thread.currentThread().getName() + " key=" + key + " value=" + value + " map size=" + dataMap.size());
            key++;
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            writeLock.unlock();
        }

    }

    class reader implements Runnable {
        private ReadWriteLockDemo readWriteLockDemo = null;
        public reader(ReadWriteLockDemo readWriteLockDemo) {
            this.readWriteLockDemo = readWriteLockDemo;
        }
        @Override
        public void run() {
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime().getTime() + "R " + Thread.currentThread().getName() + " started");
            for (int i=0; i < 10; i++) {
                readWriteLockDemo.read();
            }
        }
    }

    class writer implements Runnable {
        private ReadWriteLockDemo readWriteLockDemo;
        public writer(ReadWriteLockDemo readWriteLockDemo) {
            this.readWriteLockDemo = readWriteLockDemo;
        }
        @Override
        public void run() {
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime().getTime() + " W " + Thread.currentThread().getName() + " started");
            for (int i=0; i < 10; i++) {
                readWriteLockDemo.write();
            }
        }
    }
}
